from FunctionTool import RecursionLoop
import math,re,uuid

class Query:
    def __init__(self, dic, dspaces, dtargets, dattrs):
        self.dic = dic
        self.dspaces = dspaces
        self.dtargets = dtargets
        self.dattrs = dattrs

    def query(self, space, inputs, attrs, items):
        others=[inputs,attrs,items]
        return self._loop_parse(space, others)

    '''
    判断是否为合法的表达式
    '''
    def _IsLegal(self, val):
        results=re.findall (r'[\(\)]', val)
        k='('
        v=0
        for ret in results:
            v+=(1 if ret==k else -1)
            if v<0:
                break
        return v==0
    
    '''
    通过正则解析出嵌套的表达式
    '''
    def _parse_reg(self, val):
        reg=r'[^\)\(]*(?=\))'
        ret=re.search(reg,val)
        if ret is None:
            return False,val,None
        else:
            return True,ret.group(0),[ret.span(0)[0]-1,ret.span(0)[0]+1]

    '''
    循环迭代得到的结果
    '''
    def _loop_parse(self,val,others):
        temp = val
        temp_dict={}
        while True:
            exist,result,indexs=self._parse_reg(val)
            if not exist:
                break
            else:
                key_vals,key_ids=self._single_cmd(result, others, temp_dict)
                uid = str(uuid.uuid1())
                uid = ''.join(uid.split('-'))
                temp_dict[uid]=(key_vals,key_ids)
                temp=temp[0,indexs[0]]+uid+temp[indexs[1],len(temp)-1]
        return self._single_cmd(temp, others, temp_dict)

    def _single_cmd(self,val,others,temp_dict):
        results = val.split('|')
        #key_vals=>{"a":{"c":1}}, key_ids=>{"a_c_l":{1,2}}
        key_vals,key_ids = {},{}
        for result in results:
            if result.find ('&') == -1:
                if result not in temp_dict:
                    kvals,kids=self._atom_cmd(result,others)
                else:
                    kvals=temp_dict[result][0]
                    kids=temp_dict[result][1]
                self._merge_or(key_vals,key_ids,kvals,kids)
            else:
                rets = result.split('&')
                _key_vals,_key_ids={},{}
                for ret in rets:
                    if ret not in temp_dict:
                        kvals,kids=self._atom_cmd(ret,others)
                    else:
                        kvals=temp_dict[ret][0]
                        kids=temp_dict[ret][1]
                    self._merge_or(_key_vals,_key_ids,kvals,kids)
                self._merge_or(key_vals,key_ids,_key_vals,_key_ids)
        return key_vals,key_ids
    
    def _merge_or(self,key_vals,key_ids, other_key_vals, other_key_ids):
        if other_key_vals is None or other_key_ids is None:
            return
        for other_key,other_vals in other_key_vals.items():
            if other_key in key_vals:
                vals = key_vals[other_key]
                for other_vals_key,other_vals_val in other_vals.items():
                    if other_vals_key in vals:
                        vals[other_vals_key]+=other_vals_val
                    else:
                        vals[other_vals_key]=other_vals_val
            else:
                key_vals[other_key]=other_vals

        for other_key,other_vals in other_key_ids.items():
            if type(other_vals) == type(set()):
                if other_key in key_ids:
                    key_ids[other_key].update(other_vals)
                else:
                    key_ids[other_key]=set()|other_vals
            else:
                if other_key not in key_ids:
                    key_ids[other_key]=other_vals

    def _merge_and(self,key_vals,key_ids, other_key_vals, other_key_ids):
        if other_key_vals is None or other_key_ids is None:
            return
        del_keys=[]
        for key,vals in key_vals.items():
            if key not in other_key_vals:
                del_keys.append(key)
            else:
                other_vals=other_key_vals[key]
                val_del_keys=[]
                for val_key,val_val in vals.items():
                    if val_key not in other_vals:
                        other_vals.pop(val_key)
                    else:
                        vals[val_key]=val_val+other_vals[key]
                for _del_key in val_del_keys:
                    vals.pop(_del_key)
        for del_key in del_keys:
            key_vals.pop(del_key)

        del_keys=[]
        for key,val in key_ids.items():
            if type(val) == type(set()):
                if key not in other_key_ids:
                    del_keys.append(key)
                else:
                    val.intersection(other_key_ids[key])
        for del_key in del_keys:
            key_ids.pop(del_key)

    '''
    单个原子查询
    {"a":{"c":1}},{"a_c_l":{1,2}}
    '''
    def _atom_cmd(self, atom, others):
        array = [[atom]]+others
        darray = [[], self.dtargets, [], []]
        self._fixParam(array, darray)

        fResult,ktResult,fkDict = {},{},{}
        def action(result):
            nonlocal fResult,ktResult,fkDict
            _key = result[len(result)-1]
            iweight = 1
            if type (result[2]) == type((0,)):
                iweight = result[2][1]
                result[2] = result[2][0]

            _tResult = self._queryWithDic(result)
            if _key not in fResult:
                fResult[_key] = {}
            if _key not in fkDict:
                fkDict[_key] = {}
            _rResult = fResult[_key]
            _rfkDict = fkDict[_key]
            for _t in _tResult:
                _k = _t [0]
                _v = _t [1] * iweight
                _kt= _t [len(_t)-3]
                if _k in _rResult:
                    _v += _rResult[_k]
                _rResult[_k] = _v
                if _k not in ktResult:
                    ktResult[_k] = _kt
                setKey = "{0}_{1}_l".format(_key,_k)
                if setKey not in ktResult:
                    ktResult[setKey] = set()|_t[len(_t)-2]
                else:
                    ktResult[setKey].update(_t[len(_t)-2])

                setKey = "{0}_{1}_r".format(_key, _k)
                if setKey not in ktResult:
                    ktResult[setKey] = set()|_t[len(_t) - 1]
                else:
                    ktResult[setKey].update(_t[len(_t) - 1])

                if _k in _rfkDict:
                    _rfkDict[_k] += 1
                else:
                    _rfkDict[_k] = 0

        RecursionLoop (array, 0, None, action)
        try:
            for k,v in fkDict.items():
                for _k,_v in v.items():
                    fResult[k][_k] *= math.exp(_v)
        except Exception as e:
            print("指数错误", str(e))
        
        return fResult,ktResult
        
    def _fixParam(self, array, darray):
        i = -1
        for item in array:
            i += 1
            if item and len(item) != 0:
                continue
            array[i] = darray[i]

    def _queryWithDic(self, tkey):
        tkey = tuple(tkey)
        if tkey in self.dic:
            return self.dic[tkey]
        else:
            return []